---
title: SAIF.Application business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-08-21
library: SAIF.Application
reference_type: embedded
---

# SAIF.Application business rules

This page captures business rules enforced by this library.

## BR-APP-001 Manager construction requires a security principal

| Field | Value |
| --- | --- |
| **Statement** | An application manager cannot be created without a security principal. |
| **Trigger** | Manager construction |
| **Enforcement** | `AppManagerBase.New(inSecurityPrincipal As clsSecurityPrincipal)` — the only constructor, `Protected` |
| **Outcome** | Every manager instance carries an identity for authorization and DB client id; a principal-less construction will not compile. |
| **Source** | `SAIF.Application/AppManagerBase.vb` |

---

## BR-APP-002 Access is denied by default

| Field | Value |
| --- | --- |
| **Statement** | Access is denied by default; a manager is restricted until a security evaluation explicitly grants view or update rights. |
| **Trigger** | Manager construction / initialization |
| **Enforcement** | `Private m_AppSecurityRole As AppSecurityRole = AppSecurityRole.NoAccess` |
| **Outcome** | `IsRestricted` is `True` and `IsAccessible` is `False` until `MarkViewOnly()` or `MarkUpdate()` is called. |
| **Source** | `SAIF.Application/AppManagerBase.vb` |

---

## BR-APP-003 Access classification is mutually exclusive and read-only externally

| Field | Value |
| --- | --- |
| **Statement** | Access classification is mutually exclusive and may only be changed through the `Mark*` methods. |
| **Trigger** | Derived-class `OnCheckSecurity()` implementation |
| **Enforcement** | `MarkUpdate()`, `MarkViewOnly()`, `MarkNoAccess()` set the single private `m_AppSecurityRole`; `SecurityRole` is read-only |
| **Outcome** | Exactly one of `IsUpdateable`, `IsViewOnly`, `IsRestricted` is `True` at any time; external code cannot elevate its own rights. |
| **Source** | `SAIF.Application/AppManagerBase.vb` |

---

## BR-APP-004 AD resource authorization is cached per instance

| Field | Value |
| --- | --- |
| **Statement** | An AD resource authorization decision is evaluated once per `UserAccess` instance and reused thereafter. |
| **Trigger** | `HasAccess(resourceName)` call |
| **Enforcement** | `UserAccess.HasAccess` — cache probe on `m_AccessControl(inResourceName.ToLower)` before calling `m_Principal.IsAuthorized` |
| **Outcome** | Repeat checks return the cached decision; a mid-session AD group change is not observed until a new instance is created. Resource names are matched case-insensitively. |
| **Source** | `SAIF.Application/UserAccess.vb` |

---

## BR-APP-005 Duplicate message text is never shown twice

| Field | Value |
| --- | --- |
| **Statement** | The same message text is never shown twice in one accumulated message. |
| **Trigger** | `AppendMessageLines` call, or `MessageFormatting.GetMessages*` call |
| **Enforcement** | `If inMessage.Length > 0 AndAlso Not Message.Contains(inMessage)` in `AppManagerBase`; `If Not Results.Contains(temp)` in `MessageFormatting` |
| **Outcome** | Empty strings and duplicate texts are dropped; the user sees each distinct message once. |
| **Source** | `SAIF.Application/AppManagerBase.vb`, `SAIF.Application/MessageFormatting.vb` |

---

## BR-APP-006 A write must be bracketed by an explicit transaction

| Field | Value |
| --- | --- |
| **Statement** | A write must be bracketed by an explicit transaction start and stop, with the commit flag reflecting the write outcome.* |
| **Trigger** | `Write()` invocation on a derived manager |
| **Enforcement** | Contract declared by `OnTransactionStart()` / `OnTransactionStop(inCommit As Boolean)` `MustOverride` members; implemented in `SAIF.Application.Claim/ClaimAppManager.vb` |
| **Outcome** | All business-object saves in one `OnWrite()` share a transaction and are rolled back together on failure.* |
| **Source** | `SAIF.Application/AppManagerBase.vb` |

---

## BR-APP-007 Database client identity is the requesting user unless overridden

| Field | Value |
| --- | --- |
| **Statement** | The database client identity is the requesting user, unless an explicit batch id overrides it. |
| **Trigger** | Data-layer creation / portal creation |
| **Enforcement** | `AppManagerBase.DataLayer` passes `SecurityPrincipal.UserName.ToString`; `ApplicationDataAccess.New` and `CreateOraclePortal` / `CreateSQLServerPortal` apply `BatchID` when `BatchID.Trim.Length > 0` |
| **Outcome** | Database sessions are attributable to the end user for auditing; batch processes can substitute a service identity. |
| **Source** | `SAIF.Application/AppManagerBase.vb`, `SAIF.Application/ApplicationDataAccess.vb` |

---

## BR-APP-008 A DPI key is bound to exactly one portal

| Field | Value |
| --- | --- |
| **Statement** | Data portal handles are keyed case-insensitively and a key is bound to exactly one portal.* |
| **Trigger** | `CreateOraclePortal` / `CreateSQLServerPortal` / `InjectDPI` call |
| **Enforcement** | `m_DPILibrary.Add(inKey.ToLower, ...)` in the create methods; `If (Not m_DPILibrary.ContainsKey(inKey.ToLower))` guard in the `DPI` setter |
| **Outcome** | A first registration wins via `InjectDPI` (later ones are silently ignored), while a duplicate key through the create methods raises an `ArgumentException`.* |
| **Source** | `SAIF.Application/ApplicationDataAccess.vb` |

---

## BR-APP-009 Service responses succeed until declared otherwise, exceptions are environmental

| Field | Value |
| --- | --- |
| **Statement** | A service response is successful until a caller declares otherwise, and every exception is reported as an environmental (not business) error. |
| **Trigger** | Service operation execution / exception handling |
| **Enforcement** | `Public Result As Boolean = True` and `ProcessException` calling `AddEnvironmentMessage` |
| **Outcome** | Callers distinguish business validation failures (`Errors`) from infrastructure failures (`EnvironmentalErrors`); `Result` must be set to `False` explicitly by the caller. |
| **Source** | `SAIF.Application/Service Based/ServiceResponse.vb` |

---

## BR-APP-010 Messages render with a channel-appropriate, defaulted separator

| Field | Value |
| --- | --- |
| **Statement** | Accumulated messages are rendered with a channel-appropriate line separator, defaulting to HTML. |
| **Trigger** | Message formatting |
| **Enforcement** | `DefaultNewLineCharacter = "<BR>"` and the settable `NewLineCharacter` in `AppManagerBase`; `GetMessagesForWeb` (`<BR>`) vs. `GetMessagesForWindows` (`vbCrLf`) in `MessageFormatting` |
| **Outcome** | Web callers get `<BR>`-delimited text without change; console/Windows callers must select the `vbCrLf` variant or reset `NewLineCharacter`. |
| **Source** | `SAIF.Application/AppManagerBase.vb`, `SAIF.Application/MessageFormatting.vb` |

## Notes
- Rule IDs use the `BR-APP-` prefix assigned during analysis; no inherited (`INH-`) or stored-procedure rules apply to this library.

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with `**` could not be confirmed; treat as unknown until verified.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Security and authorization](security-authorization.md)
- [API reference](api/index.md)

## Sources
- `SAIF.Application/AppManagerBase.vb`
- `SAIF.Application/UserAccess.vb`
- `SAIF.Application/ApplicationDataAccess.vb`
- `SAIF.Application/MessageFormatting.vb`
- `SAIF.Application/Service Based/ServiceResponse.vb`
- `SAIF.Application.Claim/ClaimAppManager.vb`
